<?php

namespace App\Admin\Console\Commands;

use App\Admin\Models\SystemModel;
use App\Admin\Models\VueRouter;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Exception;

class ModelMakeCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = '
        admin:make-model
        {slug : 数据表名称，支持正则匹配}
        {--force : 强制删除}
    ';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '更新模型配置';

    /**
     * 必须存在的字段
     *
     * @var array
     */
    protected $requiredFields;

    /**
     * 数据库中的所有表名
     *
     * @var array
     */
    protected $tableNames;

    public static $updateConfirmTip = '该模型已存在配置信息，是否更新？';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        if (config()->get('app.env')=='production') {
            $this->warn('生产环境下不适用');
            return false;
        }
        $slug = $this->argument('slug');
        $this->tableNames = DB::getDoctrineSchemaManager()->listTableNames();
        $this->requiredFields = array_unique(array_merge(
            SystemModel::$searchHideField,
            SystemModel::$listReturnField,
            SystemModel::$formHideField,
            SystemModel::$detailHideField
        ));
        foreach ($this->tableNames as $key => $value) {
            if (preg_match('/'.$slug.'/', $value)) {
                $makeModelRe = $this->makeModel($value);// 创建模型
            }
        }
        $this->delModel();// 删除模型
        if (!isset($makeModelRe)) {
            $this->warn("没有查找到数据库表");
        }     
    }

    /**
     * 创建字段
     */
    protected function makeColumn($tableColumns, $systemModel, $existSystemModel)
    {
        $tableName = $systemModel['slug'];
        $modelColumns = SystemModel::withTrashed()->where('parent_id','=',$systemModel['id'])
            ->where('is_virtual',0)
            ->get(['type','validation_rules','value','slug','options','alias'])
            ->toArray();
        $modelColumns = array_column($modelColumns, NULL, 'slug');
        // 查询表的索引
        $indexes = Schema::getConnection()->getDoctrineSchemaManager()->listTableIndexes($tableName);
        $updateSum = 0;
        foreach ($tableColumns as $key => $value) {
            $appendUpdate = [];
            // 如果该模型不存在，这些字段更新
            if (!$existSystemModel) {
                $appendUpdate = array_merge($appendUpdate, [
                    'search_hide'=>(int) in_array($value->getName(), SystemModel::$listHideField),
                    'list_hide'=>(int) in_array($value->getName(), SystemModel::$listHideField),
                    'form_hide'=>(int) in_array($value->getName(), SystemModel::$formHideField),
                    'detail_hide'=>(int) in_array($value->getName(), SystemModel::$detailHideField),
                ]);
            }
            $defaultValue = $value->getDefault();
            $type = type_contrast($value->getType());
            $validationRules = column_to_rules($tableName, $indexes, $value);
            $options = null;
            // 如果是demos表，字段类型强制对照，赋默认值
            if ($systemModel['slug']=='demos') {
                $type = demos_type_contrast($value->getName());
                $defaultValue = '1';
                switch ($type) {
                    case 'single_box':
                    case 'multiple_box':
                    case 'single_select':
                    case 'multiple_select':
                    case 'transfer':
                        $optionType = $type;
                        switch ($optionType) {
                            case 'single_box':
                            case 'single_select':
                                $type = SystemModel::TYPE_SINGLE;
                                $options = [
                                    'options' => "1=>选项1\n2=>选项2",
                                    'type' => $optionType,
                                    'sample' => '1',
                                ];
                                $defaultValue = '1';
                                break;
                            case 'multiple_box':
                            case 'multiple_select':
                            case 'transfer':
                                $type = SystemModel::TYPE_MULTIPLE;
                                $options = [
                                    'options' => "1=>选项1\n2=>选项2",
                                    'type' => $optionType,
                                    'sample' => '1',
                                    'format' => 'json',
                                ];
                                $defaultValue = ["1","2"];
                                break;
                        }
                        break;
                    case 'select_tree':
                        $options = [
                            'options' => '[{"value": "11", "label": "北京市", "children": [{"value": "1101", "label": "市辖区", "children": [{"value": "110101", "label": "东城区"}] }] }]',
                            'type' => 'select',
                            'sample' => '1',
                        ];
                        $defaultValue = '110101';
                        break;
                    case 'cascader':
                        $options = [
                            'options' => '[{"value": "11", "label": "北京市", "children": [{"value": "1101", "label": "市辖区", "children": [{"value": "110101", "label": "东城区"}] }] }]',
                            'type' => 'select',
                            'sample' => '1',
                            'format' => 'json',
                        ];
                        $defaultValue = ["11","1101","110101"];
                        break;
                    case 'single_file':
                    case 'multiple_file':
                        $optionType = $type;
                        $type = SystemModel::TYPE_FILE;
                        $options = [
                            'max' => '1',
                            'ext' => null,
                        ];
                        if ($optionType=='multiple_file') {
                            $options['max'] = 2;
                        }
                        $defaultValue = null;
                        break;
                    case SystemModel::TYPE_DATETIME:
                        $defaultValue = date('Y-m-d H:i:s');
                        break;
                    case SystemModel::TYPE_DATE:
                        $defaultValue = date('Y-m-d');
                        break;
                    case SystemModel::TYPE_TIME:
                        $defaultValue = date('H:i');
                        break;
            }
            }
            $appendUpdate = array_merge($appendUpdate,[
                'type'=> $type,
                'validation_rules'=>$validationRules,
                'options'=>$options,
                'value'=>$defaultValue,
                'alias'=>$value->getComment() ?: $value->getName()
            ]);
            if (isset($modelColumns[$value->getName()])) {
                // 如果旧的 模型字段类型 与将要更新的 模型字段类型 不同，使用旧的 模型字段类型
                if ($modelColumns[$value->getName()]['type']!=$appendUpdate['type']) {
                    $appendUpdate['type'] = $modelColumns[$value->getName()]['type'];
                }
                // 如果旧的 模型字段验证默认值 与将要更新的 模型字段验证默认值 不同，使用旧的 模型字段验证默认值
                if ($modelColumns[$value->getName()]['value']!=$appendUpdate['value']) {
                    $appendUpdate['value'] = $modelColumns[$value->getName()]['value'];
                }
                // 如果旧的 模型字段选项 与将要更新的 模型字段选项 不同，使用旧的 模型字段选项
                if ($modelColumns[$value->getName()]['options']!=$appendUpdate['options']) {
                    $appendUpdate['options'] = $modelColumns[$value->getName()]['options'];
                }
                // 如果旧的 字段别名 与将要更新的 字段别名 不同，使用旧的 字段别名
                if ($modelColumns[$value->getName()]['alias']!=$appendUpdate['alias']) {
                    $appendUpdate['alias'] = $modelColumns[$value->getName()]['alias'];
                }
                // 旧的 模型字段验证规则 与将要更新的 模型字段验证规则 合并
                if ($modelColumns[$value->getName()]['validation_rules']!=$appendUpdate['validation_rules']) {
                    $appendUpdateValidationRules = explode("|", $appendUpdate['validation_rules']);
                    $modelColumnsValidationRules = explode("|", $modelColumns[$value->getName()]['validation_rules']);
                    $appendUpdate['validation_rules'] = implode("|", array_filter(array_unique(
                        array_merge(
                            $appendUpdateValidationRules,
                            $modelColumnsValidationRules
                    ))));
                }
            }
            $result = json_decode(SystemModel::withTrashed()->updateOrCreate(
                [
                    'parent_id'=>$systemModel['id'],
                    'slug'=>$value->getName()
                ],
                [
                    'parent_id'=>$systemModel['id'],
                    'name'=>$value->getComment() ?: $value->getName(),
                    'slug'=>$value->getName()
                ]+$appendUpdate
            ), true);
            if (isset($result['id'])) {
                $updateSum++;
                $this->info("字段 [ {$value->getName()} ] 更新成功");
            } else {
                $this->error("字段 [ {$value->getName()} ] 更新失败");
            }
        }
        // 如果旧的 模型字段标识不存在于将要更新的模型字段中，进行物理删除
        $diffColumns = array_diff(array_keys($modelColumns), array_keys($tableColumns));
        $deleteSum = SystemModel::withTrashed()->where('parent_id',$systemModel['id'])->whereIn('slug', $diffColumns)->forceDelete();
        if ($updateSum==count($tableColumns) && $deleteSum==count($diffColumns)) {
            foreach ($diffColumns as $diffColumn) {
                $this->warn("字段 [ {$diffColumn} ] 物理删除成功");
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * 创建模型
     */
    protected function makeModel($slug)
    {
        $this->info(PHP_EOL."更新模型 [ {$slug} ]：");
        $systemModel = SystemModel::withTrashed()
            ->where('parent_id','=',0)
            ->where('slug','=',$slug)
            ->first();   
        // 哨兵，判断是否已经存在
        $existSystemModel = false;
        if ($systemModel) {
            $existSystemModel = true;
            if (!$this->option('force') && !$this->confirm($slug.static::$updateConfirmTip, true)){
                return false;
            }
            $systemModel = $systemModel->toArray();
        } 
        // 是否逻辑删除模型
        $deleted_at = null;
        // // 如果是系统模型或者缺少必要字段，则逻辑删除
        if (in_array($slug, array_keys(SystemModel::$systemModels)) || !Schema::hasColumns($slug, $this->requiredFields)) {
            $deleted_at = date("Y-m-d H:i:s");
        }
        $table = DB::getDoctrineSchemaManager()->listTableDetails($slug);
        $columnExceptionMessages = [];
        foreach (SystemModel::$listReturnField as $field) {
            try {
                $table->getColumn($field);
            } catch (Exception $e) {
                array_push($columnExceptionMessages, $e->getMessage());
            }
        }
        if ($columnExceptionMessages) {
            foreach ($columnExceptionMessages as $columnExceptionMessage) {
                $this->warn($columnExceptionMessage);
            }
        }
        $tableColumns = $table->getColumns();
        $data = [
            'parent_id'=>0,
            'name'=>$table->getComment() ?: $table->getName(),
            'alias'=>$table->getComment() ?: $table->getName(),
            'slug'=>$slug,
            'type'=>"other",
            'desc'=>null,
            'options'=>null,
            'validation_rules'=>null,
            'value'=>null,
            'order'=>0,
            'search_hide'=>0,
            'list_hide'=>0,
            'form_hide'=>0, 
            'detail_hide'=>0, 
            'created_at'=>date("Y-m-d H:i:s"),
            'updated_at'=>date("Y-m-d H:i:s"),
            'deleted_at'=>$deleted_at,            
        ];
        DB::beginTransaction();
        $result = json_decode(SystemModel::withTrashed()->updateOrCreate(
            ['id'=>isset($systemModel['id']) ? $systemModel['id'] : 0],
            $data
        ), true);
        $makeColumnResult = $this->makeColumn($tableColumns, $result, $existSystemModel);
        $makeAdminMenuResult = $this->makeAdminMenu($result);
        if (isset($result['id']) && $makeColumnResult && $makeAdminMenuResult) {
            DB::commit();
            $this->info("模型 [ {$slug} ] 更新成功");
            return true;
        } else {
            DB::rollBack();
            $this->error("模型 [ {$slug} ] 更新失败");
            return false;
        }
    }

    /**
     * 删除模型
     * 如果旧的模型不存在于将要更新的模型中，进行物理删除
     */
    protected function delModel()
    {
        $systemModels = SystemModel::withTrashed()
            ->where('parent_id',0)
            ->pluck('slug')
            ->toArray();
        $diffModels = array_diff($systemModels, $this->tableNames);
        $diffModelIds = SystemModel::withTrashed()
            ->whereIn('slug',$diffModels)
            ->pluck('id')
            ->toArray();
        $deleteSum = SystemModel::withTrashed()->whereIn('id',$diffModelIds)
            ->orWhereIn('parent_id', $diffModelIds)->forceDelete();
        if ($deleteSum==count($diffModelIds) && $deleteSum>0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 创建后台菜单
     * 只有未删除的模型才会创建
     */
    protected function makeAdminMenu($model){
        if ($model && !$model['deleted_at']) {
            $title = str_replace('表', '', $model['name']);
            $vueRouter = app(VueRouter::class);
            $parentVueRouter = $vueRouter->updateOrCreate(
                ['title'=>'模型管理'],
                [
                    'parent_id'=>0,
                    'title'=>'模型管理',
                    'path'=>null,
                    'order'=>0,
                    'menu'=>1,
                    'icon'=>'tree',
                ]
            );
            $currentVueRouter = $vueRouter->updateOrCreate(
                ['title'=>$title.'模型'],
                [
                    'parent_id'=>$parentVueRouter->id,
                    'title'=>$title.'模型',
                    'path'=>'/system-models/'.$model['id'].'/edit',
                    'order'=>0,
                    'menu'=>1,
                ]
            );
            if ($parentVueRouter && $currentVueRouter) {
                $this->info("菜单 [ {$title}模型 ] 创建成功");
                return true;
            } else {
                $this->info("菜单 [ {$title}模型 ] 创建失败");
                return false;
            }
        }
        return true;
    }
}